/**
 * @file tasks.c
 * @author Linquan  
 * @brief 一个用于单片机的 任务和时间管理系统。
 *      使用方法：
 *          0、引入依赖："list.h、nodebuf.h"
 *          1、使用结构体 tasks_t 定义一个任务集变量
 *          2、在 定时器中断函数中调用：task_time_add
 *          3、调用 tasks_init_withbuf或者tasks_init函数进行初始化，传入 定时器中断时间
 *             (使用 @macro_Timer_interrupt_time 传入参数)。
 *          4、在需要的任务开始运行的地方 调用tasks_add_task函数添加任务。
 *              (使用 @task_delay_time 设置任务下次运行的间隔时间 )
 *          5、在不再需要任务运行的地方 调用 tasks_remove_task 从任务集中删除任务
 *          6、在主循环中调用 tasks_schedule函数
 * 
 * 
 * @version 0.6
 * @date 2024-03-28
 * 
 * @copyright Copyright (c) 2024
 * history:
 *          2024-3-28 
 *          1、增加计数器溢出控制。 
 *                    --0.3
 *          2024-5-11 
 *          1、优化任务时间计时器，将计时器归为每个任务链表结构体中。不再为全局变量。
 *          --0.4
 * 
 *          2024-12-6
 *			1、优化任务驱动框架，采用 定时器中断 时钟节拍累计的方式进行调度。
 *             当前框架最小计数tick是1us          
 *			2、 
 *             ！！！要注意，初始化时的任务的时基 要与 定时器的中断时间一致。          
 *			3、对外任务的运行时间可以使用宏定义时间进行调度
 *              任务时基可用宏：TASK_TICK_MS(X)、TASK_TICK_US(X)设置
 *			   任务的间隔运行时间，可以使用宏： MS_(X)   US_(X)进行
 *          --0.5
 * 
 *          2024-12-8
 *          1、优化 tasks框架：
 *             确定基调，这是一个用于 单片机上的任务调度框架，同时他具备在linux使用的可能，
 *             因为他本就是从linux中移植出来的
 *          2、增加 使用 宏 GET_NOW_TIME(__tasks) 来调用 tasks 自身的时间片累计时间。
 *              当前调用 get_now_time函数，返回的是 时间片数量*时间片长度，单位us   
 *              同时为将来转换为linux使用，提供修改移植标记。
 *          3、将来移植linux需要修改的地方：
 *              # GET_NOW_TIME(__tasks)：内容替换为其他的时间获取函数。
 *              # 不再在定时器中断中 使用 task_time_add 函数，即结构体tasks_t中tick_count变量不再使用
 *              # tasks_init函数中的输入参数： Timer_interupt_time 可忽略，传入宏NO_TIMER  
 *                 即tasks_t结构体中tick_time变量不再使用
 *          
 */


#include "tasks.h"
#include <inttypes.h> // PRId64
#include <stdio.h>


/**
 * @brief 获取当前时间的宏
 *  如果 需要在linux中使用，不需要定时器中断，
 *  则将该宏的内容替换为 其他获取当前时间的函数 
 */
#if USE_TIMER
    #define GET_NOW_TIME(__tasks) get_now_time(__tasks)
#else
    #define GET_NOW_TIME(__tasks) // 这里需要放 linux中获得系统时间的API，时间单位由获得的时间单位决定
#endif


/**
 * @brief 传入 需求的任务数量，由系统自动分配任务存储空间，初始化 任务框架
 * 
 * @param tasks 
 * @param ntask 
 * @param Timer_interupt_time 
 */
void tasks_init(struct tasks_t *tasks, int ntask, unsigned int Timer_interupt_time ) {
    INIT_LIST_HEAD(&tasks->tasks);
    int bytes = (ntask * sizeof(struct task_node_t)) + (ntask * sizeof(void*)) + sizeof(size_t);
    void *buf = malloc(bytes);
    nodebuf_init(&tasks->taskbuf, buf, bytes, sizeof(struct task_node_t));
    tasks->tick_time = Timer_interupt_time;
}

/**
 * @brief 传入由使用者分配的任务存储空间空间，初始化任务框架
 * 
 * @param tasks  task list head
 * @param buf     task buff with task nodes
 * @param buflen  task buff\s length
 * @param Timer_interupt_time   this value is equal Timer interrupt time ,
 *                              if use in linux,not Timer,ignore this value!
 */
void tasks_init_withbuf(struct tasks_t *tasks, void *buf, int buflen, unsigned int Timer_interupt_time ) {
    INIT_LIST_HEAD(&tasks->tasks);
    nodebuf_init(&tasks->taskbuf, buf, buflen, sizeof(struct task_node_t));
    tasks->tick_time = Timer_interupt_time;
}

/**
 * @brief 结束该任务集，释放该任务集所占用的空间
 * 
 * @param tasks 
 */
void task_fini(struct tasks_t *tasks) {
    nodebuf_fini(&tasks->taskbuf, tasks->needfree ? free : NULL);
}


 /**
  * @brief 向任务集中 添加任务
  * 
  * @param tasks        任务集
  * @param delay_time   新添加的任务延时执行时间
  * @param fn           新添加的任务 执行函数
  * @param args         传入任务参数
  */
void tasks_add_task(struct tasks_t *tasks, 
                    int delay_time, task_fn *fn, task_param_t args)
{  
    struct task_node_t *task = (struct task_node_t *) nodebuf_malloc(&tasks->taskbuf, 0);
   	
	if(delay_time > 0)
        task->exe_time = GET_NOW_TIME(tasks) + delay_time;
    task->fn = fn;
    task->param = args;

    list_add_tail(&task->list, &tasks->tasks);
}

/**
 * @brief 从任务集中删除指定任务
 * 
 * @param tasks 
 * @param fn 
 * @param once 
 * @return int 
 */
int tasks_remove_task(struct tasks_t *tasks, task_fn *fn, int once) {
    int n = 0;
    struct list_head *task_, *safe_;
    list_for_each_safe(task_, safe_, &tasks->tasks) 
    {
        struct task_node_t *task = (struct task_node_t *)task_;
        if (task->fn == fn) {
            list_del(task_);
            nodebuf_free(&tasks->taskbuf, task);
            n++;
            if (once) break;
        }
    }
    return n;
}



// 原理：无符号减法自动处理溢出，用于区分“已超时”和“未到时间”
#define TASK_IS_TIMEOUT(exe_time, now)  \
    ((TIME_TYPE)(now - exe_time) <= TIME_OVER_MAX)

// 新增：计算下次执行时间（自动处理溢出）
#define TASK_NEXT_TIME(now, delay_ms)  \
    ((TIME_TYPE)(now + delay_ms))

/**
 * @brief 任务集调度
 * 
 * @param tasks 
 */
void tasks_schedule(struct tasks_t *tasks) 
{
    struct list_head *task_, *safe_;
    TIME_TYPE now = GET_NOW_TIME(tasks);  // 获取当前时间（64位）

    list_for_each_safe(task_, safe_, &tasks->tasks) 
    {
        struct task_node_t *task = (struct task_node_t *)task_;
        
        // 修改：使用64位安全判断宏，替代直接比较
        if (task->fn && TASK_IS_TIMEOUT(task->exe_time, now))
        {
            int r_ms = task->fn(&task->param);
            
            if (r_ms > 0) 
            {
                // 修改：使用64位安全计算宏更新下次执行时间
                task->exe_time = TASK_NEXT_TIME(now, r_ms);
            } 
            else 
            {
                // 任务结束：删除并释放（原逻辑保留）
                list_del(task_);
                nodebuf_free(&tasks->taskbuf, task);
            }
        }
    }
}

#if USE_TIMER
/**
 * @brief   if use task tick to schdule,please ues this function
 *  take this function in function:tasks_schedule's current_time
 *  so it can input current tasks' running time
 *    返回任务当前运行时间：节拍数×时钟片长度  
 * @return uint32_t  current task time
 */
TIME_TYPE get_now_time(struct tasks_t *tasks)
{          
    return (tasks->tick_count * tasks->tick_time);
}

/**
 * @brief  if use task tick to schdule,please ues this function
 * take this function in Timer_Hnadle() ,so it can add by Timer interupt
 * @param tasks 
 */
void task_time_add(struct tasks_t *tasks)
{
    tasks->tick_count++;  	
}
#endif



#ifdef TEST_TASKS
void tasks_print_all(struct tasks_t *tasks, int64_t now_ms, const char *name) {
    struct list_head *task_;
    int i = 1, count = 0;
    list_for_each(task_, &tasks->tasks) { count++; }
    printf("%s tasks (count=%d now=%"PRId64"):\n", name, count, now_ms);

    list_for_each(task_, &tasks->tasks) {
        struct task_node_t *task = (struct task_node_t *)task_;
        printf("  task%d: exe_time=%"PRId64"(%+"PRId64") fn=%p i1=%d i2=%d i3=%d\n",
               i++, task->exe_time, (now_ms - task->exe_time), task->fn,
               task->args.i1, task->args.i2, task->args.i3);
    }
}


static void do_tests(struct tasks_t *tasks) {

    // just for test
    tests = tasks;
    tasks_add_task(tests, 0, task_dummy, 11, 12, 13);
    tasks_add_task(tests, 5000, task_hello, 1, 2, 3);
    tasks_add_task(tests, 0, task_dummy, 31, 32, 33);
    tasks_print_all(tests, exe_time(), "tests");
}
#endif

